package com.zenithmind.disk.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zenithmind.disk.mapper.FilesMapper;
import com.zenithmind.disk.mapper.ShareMapper;
import com.zenithmind.disk.pojo.domain.Files;
import com.zenithmind.disk.pojo.domain.Share;
import com.zenithmind.disk.pojo.dto.ShareDTO;
import com.zenithmind.disk.pojo.vo.FileVO;
import com.zenithmind.disk.pojo.vo.ShareVO;
import com.zenithmind.disk.service.FilesService;
import com.zenithmind.disk.service.ShareService;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import com.zenithmind.disk.pojo.query.ShareQuery;
import com.zenithmind.common.result.PageResult;
import org.apache.commons.lang3.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

@Service
public class ShareServiceImpl extends ServiceImpl<ShareMapper, Share> implements ShareService {

    @Autowired
    private ShareMapper shareMapper;

    @Autowired
    private FilesMapper filesMapper;

    @Autowired
    private FilesService filesService;

    @Value("${file.share.domain}")
    private String shareDomain;
    
    @Value("${file.store.path}")
    private String fileStorePath;

    /**
     * 创建文件分享链接
     *
     * @param shareDTO 包含分享文件所需信息的数据传输对象
     * @param userId   请求分享操作的用户ID
     * @return 返回一个包含分享信息的值对象
     * <p>
     * 该方法用于创建一个文件分享链接它首先验证文件的存在性和用户是否有权限进行分享，
     * 然后生成一个唯一的分享码，并创建一个分享记录如果未指定过期时间，则默认为7天后，
     * 并将文件标记为公开或非公开根据提供的信息设置提取密码最后，保存分享记录并返回一个
     * 包含所有分享详情的值对象
     */
    @Override
    @Transactional
    public ShareVO createShare(ShareDTO shareDTO, String userId) {
        // 检查文件是否存在且属于该用户
        Files file = filesMapper.selectById(shareDTO.getFileId());
        if (file == null || file.getStatus() != 1) {
            throw new RuntimeException("文件不存在");
        }

        if (!file.getUserId().equals(userId)) {
            throw new RuntimeException("无权限分享该文件");
        }

        // 生成分享码
        String shareCode = RandomUtil.randomString(6);

        // 创建分享记录
        Share share = new Share();
        share.setUserId(userId);
        share.setFileId(shareDTO.getFileId());
        share.setShareCode(shareCode);
        share.setShareUrl(shareDomain + "/share/" + shareCode);

        // 设置过期时间，根据天数计算
        LocalDateTime expireTime;
        if (shareDTO.getExpireTime() == null) {
            // 默认7天
            expireTime = LocalDateTime.now().plusDays(7);
        } else if (shareDTO.getExpireTime() <= 0) {
            // 0或负数表示永久
            expireTime = LocalDateTime.now().plusYears(100); // 设置一个很久远的日期表示永久
        } else {
            // 正常天数
            expireTime = LocalDateTime.now().plusDays(shareDTO.getExpireTime());
        }
        
        share.setExpireTime(expireTime);

        // 设置访问次数为0
        share.setAccessCount(0);

        // 设置是否公开，默认为公开
        int isPublic = shareDTO.getIsPublic();
        share.setIsPublic(isPublic);

        // 设置提取密码
        share.setPassword(shareDTO.getPassword());

        // 设置状态为有效
        share.setStatus(1);

        // 设置创建和更新时间
        share.setCreateTime(LocalDateTime.now());
        share.setUpdateTime(LocalDateTime.now());

        // 保存分享记录
        shareMapper.insert(share);

        // 转换为VO对象
        ShareVO shareVO = new ShareVO();
        BeanUtils.copyProperties(share, shareVO);
        
        // 设置文件信息
        shareVO.setFileName(file.getFileName());
        shareVO.setFileType(file.getFileType());
        shareVO.setIsDirectory(file.getIsDirectory() == 1);
        
        // 如果是文件夹，计算总大小
        if (file.getIsDirectory() == 1) {
            // 计算文件夹总大小
            long totalSize = calculateDirectorySize(file.getId(), userId);
            shareVO.setFileSize(totalSize);
        } else {
            // 普通文件使用文件自身大小
            shareVO.setFileSize(file.getFileSize());
        }

        return shareVO;
    }
    
    /**
     * 计算目录下所有文件的总大小
     * 
     * @param directoryId 目录ID
     * @param userId 用户ID
     * @return 目录下所有文件的总大小（字节）
     */
    private long calculateDirectorySize(String directoryId, String userId) {
        long totalSize = 0;
        
        // 查询目录下的所有文件和子目录
        LambdaQueryWrapper<Files> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Files::getParentId, directoryId)
                .eq(Files::getUserId, userId)
                .eq(Files::getStatus, 1);
        List<Files> filesList = filesMapper.selectList(queryWrapper);
        
        // 计算所有文件的大小总和
        for (Files file : filesList) {
            if (file.getIsDirectory() == 1) {
                // 递归计算子目录大小
                totalSize += calculateDirectorySize(file.getId(), userId);
            } else {
                // 直接加上文件大小
                totalSize += file.getFileSize();
            }
        }
        
        return totalSize;
    }

    /**
     * 根据分享码和密码获取公共分享的文件信息
     *
     * @param shareCode 分享码，用于标识特定的分享
     * @param password  分享的访问密码，用于验证访问权限
     * @return 返回文件信息的VO对象
     * @throws RuntimeException 如果分享不存在、已过期或密码错误，将抛出运行时异常
     */
    @Override
    public FileVO getPublicShare(String shareCode, String password) {
        // 查询分享信息
        LambdaQueryWrapper<Share> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Share::getShareCode, shareCode);
        Share share = shareMapper.selectOne(queryWrapper);
        if (share == null || share.getStatus() != 1) {
            throw new RuntimeException("分享不存在或已取消");
        }

        // 检查分享是否过期
        if (share.getExpireTime() != null && share.getExpireTime().isBefore(LocalDateTime.now())) {
            throw new RuntimeException("分享已过期");
        }

        // 如果需要密码但未提供或密码错误，则抛出异常
        if (!validateSharePassword(shareCode, password)) {
            throw new RuntimeException("提取密码错误");
        }

        //查询当前访问次数
        Share accessCount = shareMapper.selectById(share.getId());
        // 增加访问次数
        accessCount.setAccessCount(accessCount.getAccessCount() + 1);
        shareMapper.updateById(accessCount);

        // 获取文件信息
        FileVO fileVO = filesService.getFileInfo(share.getFileId());
        
        // 如果是文件夹，计算总大小
        if (fileVO.getIsDirectory()) {
            // 计算文件夹总大小
            long totalSize = calculateDirectorySize(fileVO.getId(), fileVO.getUserId());
            fileVO.setFileSize(totalSize);
        }
        
        return fileVO;
    }

    /**
     * 查询用户的分享列表
     *
     * @param userId 用户ID
     * @return 用户的分享列表
     */
    @Override
    public List<ShareVO> listMyShares(String userId) {
        // 查询用户的分享列表
        LambdaQueryWrapper<Share> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Share::getUserId, userId);
        List<Share> shareList = shareMapper.selectList(queryWrapper);
        List<ShareVO> shareVOList = new ArrayList<>();

        for (Share share : shareList) {
            // 排除已取消的分享
            if (share.getStatus() != 1) {
                continue;
            }

            ShareVO shareVO = new ShareVO();
            BeanUtils.copyProperties(share, shareVO);

            // 获取文件信息
            Files file = filesMapper.selectById(share.getFileId());
            if (file != null) {
                shareVO.setFileName(file.getFileName());
                shareVO.setFileType(file.getFileType());
                shareVO.setIsDirectory(file.getIsDirectory() == 1);
                
                // 如果是文件夹，计算总大小
                if (file.getIsDirectory() == 1) {
                    // 计算文件夹总大小
                    long totalSize = calculateDirectorySize(file.getId(), userId);
                    shareVO.setFileSize(totalSize);
                } else {
                    // 普通文件使用文件自身大小
                    shareVO.setFileSize(file.getFileSize());
                }
            }

            shareVOList.add(shareVO);
        }

        return shareVOList;
    }

    @Override
    @Transactional
    public boolean cancelShare(String shareId, String userId) {
        // 查询分享信息
        Share share = shareMapper.selectById(shareId);
        if (share == null || share.getStatus() != 1) {
            throw new RuntimeException("分享不存在或已取消");
        }

        // 检查是否有权限操作
        if (!share.getUserId().equals(userId)) {
            throw new RuntimeException("无权限取消该分享");
        }

        // 更新分享状态为已取消
        share.setStatus(0);
        share.setUpdateTime(LocalDateTime.now());
        shareMapper.updateById(share);

        return true;
    }

    @Override
    public boolean validateSharePassword(String shareCode, String password) {
        // 查询分享信息
        LambdaQueryWrapper<Share> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Share::getShareCode, shareCode);
        Share share = shareMapper.selectOne(queryWrapper);
        if (share == null || share.getStatus() != 1) {
            return false;
        }

        // 检查分享是否过期
        if (share.getExpireTime() != null && share.getExpireTime().isBefore(LocalDateTime.now())) {
            return false;
        }

        // 如果分享没有设置密码，无需验证，直接返回true
        if (share.getPassword() == null || share.getPassword().isEmpty()) {
            return true;
        }

        // 分享有密码，需要验证提取码
        if (password == null || password.isEmpty()) {
            return false; // 未提供密码
        }

        // 验证密码
        return share.getPassword().equals(password);
    }

    /**
     * 下载分享文件
     * @param shareCode 分享码
     * @param password 提取密码
     * @param response HTTP响应对象
     * @throws IOException 如果发生I/O错误
     */
    @Override
    public void downloadShare(String shareCode, String password, HttpServletResponse response) throws IOException {
        // 验证分享并获取文件信息
        FileVO fileVO = getPublicShare(shareCode, password);
        if (fileVO == null) {
            throw new RuntimeException("获取分享文件信息失败");
        }

        // 获取文件
        Files file = filesMapper.selectById(fileVO.getId());
        if (file == null || file.getStatus() != 1) {
            throw new RuntimeException("文件不存在或已删除");
        }

        // 处理文件夹下载
        if (file.getIsDirectory() == 1) {
            // 通过服务层调用创建并下载ZIP文件
            filesService.downloadDirectory(file.getId(), response);
            return;
        }

        // 处理单个文件下载
        File downloadFile = new File(fileStorePath + File.separator + file.getFilePath());
        if (!downloadFile.exists()) {
            throw new RuntimeException("文件不存在");
        }

        // 设置响应头
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("UTF-8");
        String fileName = URLEncoder.encode(file.getFileName(), StandardCharsets.UTF_8);
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

        // 传输文件
        try (FileInputStream fis = new FileInputStream(downloadFile);
             OutputStream os = response.getOutputStream()) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer)) > 0) {
                os.write(buffer, 0, len);
            }
            os.flush();
        }
    }

    /**
     * 下载分享文件夹中的指定文件
     * @param shareCode 分享码
     * @param fileId 文件ID
     * @param password 提取密码
     * @param response HTTP响应对象
     * @throws IOException 如果发生I/O错误
     */
    @Override
    public void downloadSharedFile(String shareCode, String fileId, String password, HttpServletResponse response) throws IOException {
        // 验证分享
        if (!validateSharePassword(shareCode, password)) {
            throw new RuntimeException("分享密码错误或分享不存在");
        }

        // 获取分享信息
        LambdaQueryWrapper<Share> shareQueryWrapper = new LambdaQueryWrapper<>();
        shareQueryWrapper.eq(Share::getShareCode, shareCode);
        Share share = shareMapper.selectOne(shareQueryWrapper);
        if (share == null || share.getStatus() != 1) {
            throw new RuntimeException("分享不存在或已取消");
        }

        // 检查分享是否过期
        if (share.getExpireTime() != null && share.getExpireTime().isBefore(LocalDateTime.now())) {
            throw new RuntimeException("分享已过期");
        }

        // 获取要下载的文件
        Files file = filesMapper.selectById(fileId);
        if (file == null || file.getStatus() != 1) {
            throw new RuntimeException("文件不存在或已删除");
        }

        // 检查文件是否在分享的目录树下
        if (!isFileInShareTree(share.getFileId(), fileId, share.getUserId())) {
            throw new RuntimeException("文件不在分享目录下");
        }

        // 处理文件夹下载
        if (file.getIsDirectory() == 1) {
            // 通过服务层调用创建并下载ZIP文件
            filesService.downloadDirectory(file.getId(), response);
            return;
        }

        // 处理单个文件下载
        File downloadFile = new File(fileStorePath + File.separator + file.getFilePath());
        if (!downloadFile.exists()) {
            throw new RuntimeException("文件不存在");
        }

        // 设置响应头
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("UTF-8");
        String fileName = URLEncoder.encode(file.getFileName(), StandardCharsets.UTF_8);
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

        // 传输文件
        try (FileInputStream fis = new FileInputStream(downloadFile);
             OutputStream os = response.getOutputStream()) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer)) > 0) {
                os.write(buffer, 0, len);
            }
            os.flush();
        }
    }

    /**
     * 检查文件是否在分享目录树下
     * @param shareRootId 分享根目录ID
     * @param fileId 要检查的文件ID
     * @param userId 用户ID
     * @return 是否在分享目录树下
     */
    @Override
    public boolean isFileInShareTree(String shareRootId, String fileId, String userId) {
        // 如果文件ID与分享根目录ID相同，直接返回true
        if (shareRootId.equals(fileId)) {
            return true;
        }

        // 获取要检查的文件
        Files file = filesMapper.selectById(fileId);
        if (file == null || file.getStatus() != 1 || !file.getUserId().equals(userId)) {
            return false;
        }

        // 如果parentId为null或空，表示已经到达根目录，但仍未找到匹配，返回false
        if (file.getParentId() == null || file.getParentId().isEmpty()) {
            return false;
        }

        // 递归检查父目录
        return isFileInShareTree(shareRootId, file.getParentId(), userId);
    }

    /**
     * 分页查询分享列表
     *
     * @param shareQuery 分享查询对象，包含查询参数和分页信息
     * @return 返回分页查询结果，包含分享列表和分页相关数据
     */
    @Override
    public PageResult<ShareVO> sharePage(ShareQuery shareQuery) {
        Page<Share> page = shareQuery.toPage();

        Page<Share> sharePage = lambdaQuery()
            .eq(StringUtils.isNotBlank(shareQuery.getUserId()), Share::getUserId, shareQuery.getUserId())
            .eq(shareQuery.getStatus() != null, Share::getStatus, shareQuery.getStatus())
            .orderByDesc(Share::getCreateTime)
            .page(page);

        // 转换为VO对象
        List<ShareVO> shareVOList = sharePage.getRecords().stream().map(share -> {
            ShareVO shareVO = new ShareVO();
            BeanUtils.copyProperties(share, shareVO);
            
            // 获取文件信息
            if (StringUtils.isNotBlank(share.getFileId())) {
                Files file = filesService.getById(share.getFileId());
                if (file != null) {
                    shareVO.setFileName(file.getFileName());
                    shareVO.setFileType(file.getFileType());
                    shareVO.setIsDirectory(file.getIsDirectory() == 1);
                }
            }
            
            return shareVO;
        }).collect(Collectors.toList());

        return new PageResult<>(shareVOList, sharePage.getTotal(), sharePage.getCurrent(), sharePage.getSize());
    }

    /**
     * 批量删除分享-管理员
     *
     * @param ids 分享ID列表
     * @return 是否成功
     */
    @Override
    @Transactional
    public boolean deleteBatchShares(List<String> ids) {
        if (ids == null || ids.isEmpty()) {
            return true;
        }

        // 查询要删除的分享记录
        List<Share> shares = listByIds(ids);
        if (shares.isEmpty()) {
            return true;
        }

        // 更新分享状态为已取消
        for (Share share : shares) {
            share.setStatus(0);
            share.setUpdateTime(LocalDateTime.now());
        }
        
        // 批量更新
        updateBatchById(shares);

        return true;
    }

    /**
     * 管理员添加或修改分享
     *
     * @param share 分享信息
     * @return 分享信息
     */
    @Override
    @Transactional
    public ShareVO addOrUpdateShareByAdmin(Share share) {
        // 如果是修改操作
        if (StringUtils.isNotBlank(share.getId())) {
            Share existingShare = getById(share.getId());
            if (existingShare == null) {
                throw new RuntimeException("分享记录不存在");
            }
            
            // 保存修改后的分享信息
            share.setUpdateTime(LocalDateTime.now());
            updateById(share);
        } else {
            // 添加操作，检查文件是否存在
            Files file = filesMapper.selectById(share.getFileId());
            if (file == null || file.getStatus() != 1) {
                throw new RuntimeException("关联的文件不存在或已删除");
            }
            
            // 生成分享码
            if (StringUtils.isBlank(share.getShareCode())) {
                share.setShareCode(RandomUtil.randomString(6));
            }
            
            // 设置分享URL
            if (StringUtils.isBlank(share.getShareUrl())) {
                share.setShareUrl(shareDomain + "/share/" + share.getShareCode());
            }
            
            // 设置默认值
            if (share.getExpireTime() == null) {
                share.setExpireTime(LocalDateTime.now().plusDays(7)); // 默认7天
            }
            
            if (share.getAccessCount() == null) {
                share.setAccessCount(0);
            }
            
            if (share.getIsPublic() == null) {
                share.setIsPublic(1); // 默认公开
            }
            
            if (share.getStatus() == null) {
                share.setStatus(1); // 默认有效
            }
            
            share.setCreateTime(LocalDateTime.now());
            share.setUpdateTime(LocalDateTime.now());
            
            save(share);
        }
        
        // 返回更新后的分享信息
        Share updatedShare = getById(share.getId());
        ShareVO shareVO = new ShareVO();
        BeanUtils.copyProperties(updatedShare, shareVO);
        
        // 设置文件信息
        Files file = filesMapper.selectById(updatedShare.getFileId());
        if (file != null) {
            shareVO.setFileName(file.getFileName());
            shareVO.setFileType(file.getFileType());
            shareVO.setIsDirectory(file.getIsDirectory() == 1);
            
            // 设置文件大小
            if (file.getIsDirectory() == 1) {
                shareVO.setFileSize(calculateDirectorySize(file.getId(), file.getUserId()));
            } else {
                shareVO.setFileSize(file.getFileSize());
            }
        }
        
        return shareVO;
    }
}
